from .utils import *
from typing import Dict, List, Any, Callable, Optional
from .tool import Tool
from pathlib import Path
import sys
import json

class ToolsRegistry:
    def __init__(self):
        self.tools:Dict[str, Tool] = {}
        self._loadBuiltinTools_()

    def toolsPrompt(self) -> str:
        EnhancedOutput.display(f"应用工具提示词", OutputType.PLANNING)
        tools = self.getTools()
        if tools:
            tools_prompt = "Avaiable tools:\n"
            for tool in tools:
                EnhancedOutput.display(f"{tool['name']}: {tool['description']}", OutputType.INFO)
                tools_prompt += f"- name: {tool['name']}\n"
                tools_prompt += f"  description: {tool['description']}\n"
                tools_prompt += f"  parameters: {tool['parameters']}\n"
            tools_prompt += """
        Tool Usage Format:

    <TOOL_CALL>
    name: tool_name
    arguments:
        param1: value1
        param2: value2
    </TOOL_CALL>
    ---------------------------------------------
    """
            return tools_prompt
        else:
            return ""

    def _loadBuiltinTools_(self):
        """加载内置工具"""
        tools_dir = Path(__file__).parent
        for tool_file in tools_dir.glob("*.py"):
            if tool_file.name in ["tool.py", "__init__.py", "toolsregistry.py", "utils.py"]:
                continue
            self.loadToolByFile(tool_file)


    def loadToolByFile(self, tool_file:Path):
        """加载工具文件"""
        try:
            if not tool_file.exists() or not tool_file.is_file():
                EnhancedOutput.display(f"工具文件不存在或不是文件: {tool_file}", OutputType.ERROR)
                return False
            parent_dir = str(tool_file.parent)
            sys.path.insert(0, parent_dir)

            try:
                modele_name =  tool_file.stem
                module = __import__(modele_name)
                
                tool_found = False
                for item_name in dir(module):
                    item = getattr(module, item_name)
                    if (isinstance(item, type) and
                        hasattr(item, "name") and 
                        hasattr(item, "description") and 
                        hasattr(item, "parameters") and 
                        hasattr(item, "execute")):

                        if hasattr(item, "check"):
                            if not item.check():
                                EnhancedOutput.display(f"Tool {item.name} check failed, skipping", OutputType.INFO)
                                continue

                        tool_instance = item()

                        self.registerTool(name=tool_instance.name, 
                                          description=tool_instance.description, 
                                          parameters=tool_instance.parameters, 
                                          func=tool_instance.execute)
                        
                        tool_found = True
                        break
                
                if not tool_found:
                    EnhancedOutput.display(f"没发现工具： {tool_instance.name} 已加载", OutputType.INFO)
                    return False
                
            finally:
                sys.path.remove(parent_dir)

        except Exception as e: 
            EnhancedOutput.display(f"加载工具文件失败: {tool_file}", OutputType.ERROR)
            return False
        

    def registerTool(self, name:str, description:str, parameters:Dict, func:Callable):
        self.tools[name] = Tool(name, description, parameters, func)


    def getTool(self, name:str) ->Optional[Tool]:
        return self.tools.get(name)
    
    def getTools(self) -> List[Dict]:
        return [tool.toDict() for tool in self.tools.values()]
    
    def executeTool(self, name:str, arguments:Dict) -> Dict[str, Any]:
        tool = self.getTool(name)
        if tool is None:
            return {
                "success":False,
                "stdout": "",
                "stderr":f"Tool {name} not found"
            }
        return tool.execute(arguments)

    def parseToolCalls(self, text: str) -> List[Dict]:
        """Parse tool calls from text response"""
        import re
        
        tool_calls = []
        # Match <TOOL_CALL> blocks - 更宽松的正则表达式
        pattern = r'<TOOL_CALL>(.*?)</TOOL_CALL>'
        matches = re.findall(pattern, text, re.DOTALL)
        
        for match in matches:
            try:
                # 提取name和arguments部分
                name_match = re.search(r'name:\s*(\w+)', match, re.IGNORECASE)
                if not name_match:
                    continue
                    
                name = name_match.group(1)
                
                # 解析arguments部分
                args = {}
                # 查找arguments块
                args_match = re.search(r'arguments:\s*\n(.*?)(?=\n\w+|$)', match, re.DOTALL | re.IGNORECASE)
                if args_match:
                    args_text = args_match.group(1).strip()
                    # 解析参数行
                    for line in args_text.split('\n'):
                        line = line.strip()
                        if ':' in line and not line.startswith('#'):
                            try:
                                key, value = line.split(':', 1)
                                args[key.strip()] = value.strip()
                            except ValueError:
                                continue
                
                tool_calls.append({
                    "name": name.strip(),
                    "arguments": args
                })
            except Exception as e:
                EnhancedOutput.display(f"Failed to parse tool call: {match}, error: {e}", OutputType.ERROR)
        
        return tool_calls

    def handleToolCalls(self, tool_calls: List[Dict]) -> str:
        """Handle tool calls, only process the first tool"""
        try:
            if not tool_calls:
                return ""
                
            # Only process the first tool call
            tool_call = tool_calls[0]
            name = tool_call["name"]
            args = tool_call["arguments"]

            tool_call_help = """
Tool Usage Format:

<TOOL_CALL>
name: tool_name
arguments:
    param1: value1
    param2: value2
</TOOL_CALL>
"""
            
            if isinstance(args, str):
                try:
                    args = json.loads(args)
                except json.JSONDecodeError:
                    EnhancedOutput.display(f"Invalid tool parameters format: {name} {tool_call_help}", OutputType.ERROR)
                    return ""

            # Display tool call information
            EnhancedOutput.showHeader(f"Executing tool: {name}", OutputType.TOOL)
            if isinstance(args, dict):
                for key, value in args.items():
                    EnhancedOutput.display(f"Parameter: {key} = {value}", OutputType.DEBUG)
            else:
                EnhancedOutput.display(f"Parameter: {args}", OutputType.DEBUG)
            
            # Execute tool call
            result = self.executeTool(name, args)

            stdout = result["stdout"]
            stderr = result.get("stderr", "")
            output_parts = []
            if stdout:
                output_parts.append(f"Output:\n{stdout}")
            if stderr:
                output_parts.append(f"Error:\n{stderr}")
            output = "\n\n".join(output_parts)
            output = "no output and error" if not output else output
            
            # Process the result
            if result["success"]:
                EnhancedOutput.showHeader("Execution successful", OutputType.SUCCESS)
            else:
                EnhancedOutput.showHeader("Execution failed", OutputType.WARNING)
            
            if len(tool_calls) > 1:
                output += f"\n\n--- Only one tool can be executed at a time, the following tools were not executed\n{str(tool_calls[1:])} ---"
            return output
            
        except Exception as e:
            EnhancedOutput.display(f"Tool execution failed: {str(e)}", OutputType.ERROR)
            return f"Tool call failed: {str(e)}"
